﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using Netfraction.Common.Logging;

namespace NetfractionHub.Network
{
	public class ConnectionListner
	{
		List<Socket> _listnerSockets;

		#region Singleton Pattern
		// Use singleton pattern.
		private static ConnectionListner _instance;

		/// <summary>
		/// Obtain singleton instance of ConnectionManager
		/// </summary>
		/// <returns></returns>
		public static ConnectionListner GetInstance()
		{
			// Check instance
			if (ConnectionListner._instance == null)
			{
				// Initialise instance
				ConnectionListner._instance = new ConnectionListner();
			}

			// Return instance
			return ConnectionListner._instance;
		}
		#endregion Singleton Pattern

		public void Start()
		{
				_listnerSockets = new List<Socket>();

				foreach (EndPoint ep in Settings.HubSettings.HubConnection.ListeningEndPoints)
				{
					try
					{
						if (ep is DnsEndPoint)
						{
							DnsEndPoint dep = (ep as DnsEndPoint);
							foreach (var ip in Dns.GetHostAddresses(dep.Host))
							{
								AddListener(new IPEndPoint(ip, dep.Port));
							}
						}
						else if (ep is System.Net.IPEndPoint)
						{
							AddListener(ep as IPEndPoint);
						}
					}
					catch (ObjectDisposedException)
					{
						;
					}
					catch (SocketException ex)
					{
						LogManager.WriteEntry(String.Format("Socket exception: {0}", ex), LogEntryType.Error, LogEventId.ConnectionListenerError, LogLevel.Basic);
					}
				}
		}

		public Socket AddListener(IPEndPoint iep)
		{
			try
			{
				Socket tempSocket = null;

				// Initialize Socket
				tempSocket = new Socket(iep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

				// Bind Socket To Port
				tempSocket.Bind(iep);

				// Listen
				tempSocket.Listen(128);

				// Start Async Operation
				tempSocket.BeginAccept(new AsyncCallback(EndAccept), tempSocket);

				_listnerSockets.Add(tempSocket);

				LogManager.WriteEntry(String.Format("Listening for connections on endpoint: {0}", iep.ToString()), LogEventId.ConnectionListenerStarted, LogLevel.Basic);

				return tempSocket;
			}
			catch (Exception)
			{
				return null;
			}
		}

		/// <summary>
		/// This is the call back function, which will be invoked when a client is connected
		/// </summary>        
		private void EndAccept(IAsyncResult asyn)
		{
			try
			{
				// End Socket Accept
				Task.Factory.StartNew(() => ConnectionManager.OnConnectClient((asyn.AsyncState as Socket).EndAccept(asyn)));
			}
			catch (ObjectDisposedException)
			{
				// Exceptions are bad; but if we return here, the hub will stop listening for connections on this port, and that is worse!
			}
			catch (SocketException ex)
			{
				// this can happen if the network switch dies it appears.. you get a 'network connection was forcibly terminated by a remote host
				LogManager.WriteEntry(String.Format("Socket exception: {0}", ex), LogEntryType.Error, LogEventId.ConnectionListenerError, LogLevel.Basic);
			}
			
			// Start Async Operation Again
			(asyn.AsyncState as Socket).BeginAccept(EndAccept, (asyn.AsyncState as Socket));
		}
	}
}